package main

import (
	"device/dao"
	"device/model"
	"device/util"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/emmansun/gmsm/sm3"
	"github.com/gogf/gf/v2/net/gtcp"
	"github.com/gogf/gf/v2/net/gudp"
	"io"
	"log"
	"os/exec"
	"runtime"
	"time"
)

// 模拟

var defaultUID = []byte{0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38}
var key = "1234567890ABCDEF1234567890ABCDEF"

func main() {
	//keycenterIp := "192.168.146.12:8990"
	s := flag.String("ip", "127.0.0.1:9000", "网关ip")
	etcdif := flag.Bool("e", false, "是否开启etcd")
	flag.Parse()
	ip, port, err := util.SplitIPAndPort(*s)
	if err != nil {
		panic(err)
	}
	go proxy(ip, port) //模拟测试，网关对外服务提供地址为本机port+1000,模拟转发地址为192.168.146.11:11000（）
	err = ListenClient(*s, *etcdif, util.ToServerPort(port)).Run()
	if err != nil {
		fmt.Println("ListenClient err", err)
	}
}

// ListenClient 监听客户端servicePort为需要开关的端口
func ListenClient(ip string, etcdif bool, servicePort string) *gudp.Server {
	//阻塞等待选举为leader，同时获取redis缓存的进行主从复制
	if etcdif {
		registerGatawayServer(ip)
	}
	fmt.Println("开启零信任网关", ip)
	s := gudp.NewServer(ip, func(conn *gudp.Conn) {
		for {
			recvBytes, err := conn.Recv(4096)
			if err != nil {
				panic(err)
			}

			device := new(model.Device)
			json.Unmarshal(recvBytes, device)
			//fmt.Println("device", device)
			switch device.MessageType {
			case model.Certificate: //请求证书
				fmt.Println("请求证书")
				keycenterIp := "192.168.146.12:8990"
				if etcdif {
					keycenterIp = util.GetKeyCenterLeader("192.168.146.1:2379")
				}
				fmt.Println("Gata处理请求证书")
				CerHandler(conn, device, keycenterIp)
			case model.SPA: //SPA包认证
				fmt.Println("device发送SPA包认证")
				doit := SPAHandler(conn, device)
				fmt.Println("是否通过验证：", doit)
				if doit {
					fmt.Println("开启服务端口")
					openPort(servicePort)
					go closePort(servicePort)
				}
			}
		}
	})
	fmt.Println("listen client at ", ip)
	return s
}

func PemRequest(model *model.KeyGenerateMessage) []byte { //发送请求，一个是
	return model.ToBytes()
}

// SPAHandler 验证单包信息是否合法
func SPAHandler(conn *gudp.Conn, device *model.Device) bool {
	excite, s := dao.IfDeviceExcite(device.ToDeviceInfo())
	if !excite {
		log.Println(s)
		return false
	}

	//fmt.Println("device.ToDeviceInfo()")
	certificate := dao.GetCertificate(device.ToDeviceInfo().Token)
	//fmt.Println("数据库中存在键!!!", device)
	//If := device.PemExcitePublic()
	//spapackage := new(SPAPacket)
	AuthHashMap := make(map[string]string)
	err := json.Unmarshal(device.Data, &AuthHashMap)

	if err != nil {
		panic(err)
	}
	spaJSON := AuthHashMap["SPAJson"]
	cipherText := AuthHashMap["密文"]
	SM3MAC := AuthHashMap["摘要"]
	sign := AuthHashMap["签名"]

	fmt.Println("-----------------零信任网关执行操作--------------")
	//fmt.Println("AuthHashMap", AuthHashMap)
	//fmt.Println("AuthHashMap结束——————————————————")
	//fmt.Println("AuthHashMap:", AuthHashMap)
	// 第一步验证客户端的签名是否正确
	fmt.Println("第一步开始验证签名信息：")
	fmt.Println("验签")
	pu := device.ReadPublicFromToken(certificate.Certificate)
	b := verifySM2Signature(pu, spaJSON, sign)
	fmt.Println("验签结果:", b)
	if b == false {
		return false
	}
	fmt.Println("--签名测试结束--")
	//fmt.Println()

	// 第二步解密客户端的加密单包信息
	fmt.Println("第二步解密客户端的加密单包信息")
	sm4 := SM4Utils{secretKey: key, hexString: true}
	plainText, _ := sm4.decryptDataECB(cipherText)
	fmt.Println("SPA密文：", cipherText)
	fmt.Println("解密结果: ", plainText)

	// 第三步验证客户端的摘要信息
	fmt.Println("第三步验证客户端的摘要信息")
	strBytes := []byte(plainText)
	md := sm3.Sum(strBytes)
	//md := sha256.Sum256()
	ss := hex.EncodeToString(md[:])
	SM3MAC2 := ss
	fmt.Println("客户端发送的单包信息摘要结果：", SM3MAC)
	fmt.Println("零信任网关自己计算的单包信息的摘要结果为：", SM3MAC2)
	if SM3MAC == SM3MAC2 {
		fmt.Println("SM3摘要结果一致，验证成功")
	} else {
		fmt.Println("校验失败")
		return false
	}
	fmt.Println()

	// 第四步，验证SPA数据包中的信息
	fmt.Println("第四步，验证SPA数据包中的信息")
	var j1 map[string]interface{}
	json.Unmarshal([]byte(plainText), &j1)
	fmt.Println("输出单包数据信息：")
	for key, value := range j1 {
		fmt.Println(key+":", value)
	}
	return true

}

func CerHandler(conn *gudp.Conn, device *model.Device, keycenterip string) {
	excite, s := dao.IfDeviceExcite(device.ToDeviceInfo())
	if !excite {
		log.Println(s)
		return
	}
	fmt.Println("数据库中存在键!!!", device)

	//检测是否已经存在了证书
	//_, err := os.Stat("./key/" + device.Token + ".pem")
	//if err == nil {
	//	log.Println("证书存在，不进行操作", err)
	//	return
	//}
	certificate := dao.GetCertificate(device.Token)
	if certificate.ID != 0 {
		log.Println("证书存在，不进行操作")
		return
	}
	//证书不存在
	requestBytes := PemRequest(&model.KeyGenerateMessage{
		ID:     device.ID,
		Token:  device.Token,
		IPADDR: conn.RemoteAddr().String(),
	})
	//请求密钥生成中心生成证书
	newConn, err := gtcp.NewConn(keycenterip)
	//fmt.Println("建立连接？")
	if err != nil {
		log.Println("???????????????", s)
		return
	}
	pkg, err := newConn.SendRecv(requestBytes, -1)
	//fmt.Println("发送了消息", string(requestBytes))
	if err != nil && err != io.EOF {
		log.Println(err)
		return
	}
	//TODO:写证书文件逻辑
	dao.SetCertificate(device.Token, string(pkg))
	return
}

func openPort(port string) {
	system := runtime.GOOS
	if system == "linux" { //linux开端口
		cmd := exec.Command("firewall-cmd", "--permanent", "--zone=public", "--add-port="+port+"/tcp")
		err := cmd.Run()
		if err != nil {
			fmt.Println("Error opening port ", port, err)
		} else {
			fmt.Println("Port " + port + " opened successfully")
		}
		// 重启firewalld以应用更改
		restartFirewallCmd := exec.Command("firewall-cmd", "--reload")
		err = restartFirewallCmd.Run()
		if err != nil {
			fmt.Println("Error restarting firewalld:", err)
		} else {
			fmt.Println("firewalld restarted successfully")
		}
	} else if system == "windows" { //windows 开端口
		//清除端口原逻辑
		cmdBefore := exec.Command("netsh", "advfirewall", "firewall", "delete", "rule", "name=BlockPort"+port)
		output, err := cmdBefore.CombinedOutput()
		if err != nil {
			fmt.Println("cmdBefore.CombinedOutput", err)
		}
		//执行新逻辑
		cmd := exec.Command("netsh", "advfirewall", "firewall", "add", "rule", "name=OpenPort"+port, "dir=in", "action=allow", "protocol=TCP", "localport="+port)
		output, err = cmd.CombinedOutput()
		if err != nil {
			fmt.Printf("Error opening port %s: %s\n", port, err)
		} else {
			fmt.Printf("Port %s opened successfully. Output: %s\n", port, output)
		}
	}
}

func closePort(port string) {
	time.Sleep(30 * time.Second)
	system := runtime.GOOS
	if system == "linux" {
		cmd := exec.Command("firewall-cmd", "--permanent", "--zone=public", "--remove-port="+port+"/tcp")
		err := cmd.Run()
		if err != nil {
			fmt.Println("Error closing port ", port, err)
		} else {
			fmt.Println("Port " + port + " closed successfully")
		}
		// 重启firewalld以应用更改
		restartFirewallCmd := exec.Command("firewall-cmd", "--reload")
		err = restartFirewallCmd.Run()
		if err != nil {
			fmt.Println("Error restarting firewalld:", err)
		} else {
			fmt.Println("firewalld restarted successfully")
		}
	} else if system == "windows" {
		//
		cmdBefore := exec.Command("netsh", "advfirewall", "firewall", "delete", "rule", "name=OpenPort"+port)
		output, err := cmdBefore.CombinedOutput()
		if err != nil {
			fmt.Println("cmdBefore.CombinedOutput", err)
		}
		//
		cmd := exec.Command("netsh", "advfirewall", "firewall", "add", "rule", "name=BlockPort"+port, "dir=in", "action=block", "protocol=TCP", "localport="+port)
		output, err = cmd.CombinedOutput()
		if err != nil {
			fmt.Printf("Error closing port %s: %s\n", port, err)
		} else {
			fmt.Printf("Port %s closed successfully. Output: %s\n", port, output)
		}
	}
}
